package de.gee.erep.server.xml;

import java.io.BufferedInputStream;
import java.io.IOException;
import java.net.URL;
import java.util.LinkedList;
import java.util.List;

import javax.xml.stream.XMLEventReader;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.events.XMLEvent;
import javax.xml.transform.stream.StreamSource;

import de.gee.erep.shared.entities.country.Country;
import de.gee.erep.shared.entities.country.CountryPars;
import de.gee.erep.shared.entities.country.Region;

/**
 * @author mgrefe
 */
public class CountryParser extends ErepParser<Country> {
	/***/
	private static final int MAXREGIONSTRING = 10;
	/***/
	private static final String REGION = "region";
	/***/
	private static final String COUNTRY = "country";
	/***/
	private static final String COMPOSITE = "regions";
	/***/
	private static final String ROOT = "countries";
	/***/
	private final String filename;

	/**
	 * @param filename
	 *            f
	 */
	public CountryParser(final String filename) {
		super();
		this.filename = filename;
	}
	public CountryParser(){
		this(null);
	}
	
	private final void parseUpdate() throws XMLStreamException, IOException {
		XMLInputFactory input = XMLInputFactory.newInstance();
		BufferedInputStream stream = new BufferedInputStream(new URL(
				"http://api.erepublik.com/v2/feeds/countries").openStream());
		XMLEventReader evRd = input.createXMLEventReader(stream);
		while (evRd.hasNext()) {
			XMLEvent ev = evRd.nextEvent();
			if (ev.isStartElement()) {
				String elementName = ev.asStartElement().getName()
						.getLocalPart();
				if ((elementName.equals(ROOT))) {
					continue;
				}
				if (elementName.equals(COUNTRY)) {
					parseCountry(evRd);
				}
			}
		}
	}

	@Override
	public final void parse() throws XMLStreamException {
		if(filename == null){
			try {
				parseUpdate();
			} catch (IOException e) {
				e.printStackTrace();
			}
			return;
		}
		XMLInputFactory input = XMLInputFactory.newInstance();
		XMLEventReader evRd = input.createXMLEventReader(new StreamSource(
				filename));
		while (evRd.hasNext()) {
			XMLEvent ev = evRd.nextEvent();
			if (ev.isStartElement()) {
				String elementName = ev.asStartElement().getName()
						.getLocalPart();
				if ((elementName.equals(ROOT))) {
					continue;
				}
				if (elementName.equals(COUNTRY)) {
					parseCountry(evRd);
				}
			}
		}
	}

	/**
	 * 
	 * @param evRd
	 *            the eventreader
	 * @throws XMLStreamException
	 *             ex
	 */
	private void parseCountry(final XMLEventReader evRd)
			throws XMLStreamException {
		List<Region> regions = new LinkedList<Region>();
		CountryPars ps = new CountryPars();
		String elementName = "";
		while (evRd.hasNext()) {
			XMLEvent ev = evRd.nextEvent();
			if (ev.isStartElement()) {
				elementName = ev.asStartElement().getName().getLocalPart();
				if (elementName.equals(COMPOSITE)) {
					parseRegions(evRd, regions);
					ps.regions = regions;
					continue;
				}
			}
			if (ev.isCharacters()) {
				String characterData = ev.asCharacters().getData();
				setCountryPars(ps, elementName, characterData);
			}
			if (ev.isEndElement()) {
				if (ev.asEndElement().getName().getLocalPart().equals(COUNTRY)) {
					this.getResult().add(new Country(ps));
					regions = new LinkedList<Region>();
				}
			}
		}
	}

	/**
	 * 
	 * @param ps
	 *            ps
	 * @param elementName
	 *            ename
	 * @param characterData
	 *            cdata
	 */
	private void setCountryPars(final CountryPars ps, final String elementName,
			final String characterData) {
		if (characterData.trim().length() > 0) {
			if (elementName.contains("region-")) {
				ps.regionCount = Integer.valueOf(characterData);
			} else if (elementName.contains("average")) {
				ps.averageCitizenLevel = Integer.valueOf(characterData);
			} else if (elementName.contains("citizen-co")) {
				ps.citizenCount = Integer.valueOf(characterData);
			} else if (elementName.contains("code")) {
				ps.countryCode = characterData;
			} else if (elementName.contains("contin")) {
				ps.continent = characterData;
			} else if (elementName.contains("curr")) {
				ps.currency = characterData;
			} else if (elementName.contains("name")) {
				ps.name = characterData;
			} else if (elementName.contains("citizen-fee")) {
				ps.citizenFee = Integer.valueOf(characterData);
			} else if (elementName.contains("id")) {
				ps.id = Integer.valueOf(characterData);
			}
		}
	}

	/**
	 * Parses Regions from country-File.
	 * 
	 * @param evRd
	 *            the reader
	 * @param regions
	 *            the list of regions the event
	 * @throws XMLStreamException
	 *             e
	 */
	private void parseRegions(final XMLEventReader evRd,
			final List<Region> regions) throws XMLStreamException {
		String[] rs = new String[MAXREGIONSTRING];
		XMLEvent e;
		int j = 0;
		while (evRd.hasNext()) {
			e = evRd.nextEvent();
			if (e.isEndElement()) {
				if (e.asEndElement().getName().getLocalPart().equals(COMPOSITE)) {
					break;
				}
				if (e.asEndElement().getName().getLocalPart().equals(REGION)) {
					int i = 0;
					for (String string : rs) {
						if (string == null) {
							break;
						}
						i++;
					}
					if (i > 2) {
						rs = concatStrings(rs, i);
					}
					Region r = new Region(Integer.valueOf(rs[1]), rs[0]);
					regions.add(r);
					j = 0;
				}
			}
			if (e.isCharacters()) {
				String s = e.asCharacters().getData();
				if (s.trim().length() > 0) {
					rs[j++] = s;
				}
			}
		}
	}

	/**
	 * 
	 * @param rs
	 *            rs
	 * @param off
	 *            offset
	 * @return String[]
	 */
	private String[] concatStrings(final String[] rs, final int off) {
		String[] result = new String[2];
		result[1] = rs[off - 1];
		StringBuilder s = new StringBuilder();
		for (int i = 0; i < rs.length; i++) {
			if (i == off - 1) {
				break;
			}
			s.append(rs[i]);
		}
		result[0] = s.toString();
		return result;
	}
}
